package com.yoki.cloud.common.code.generator;

import com.yoki.cloud.common.code.db.property.DBTableProperty;
import com.yoki.cloud.common.code.bean.EntityBean;
import com.yoki.cloud.common.code.bean.Transformer;
import com.yoki.cloud.common.code.db.builder.DBTableBuilder;
import freemarker.template.Configuration;
import freemarker.template.Template;

import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.*;

/**
 * @author Sky$
 * @Description: TODO
 * @date 2018/1/24$ 10:13$
 */
public abstract class AbstractCodeGenerator {

    public static final String DB_DRIVER_CLASS = "com.mysql.jdbc.Driver";
    /**
     * 数据库链接
     */
    public static final String DB_URL = "jdbc:mysql://localhost:3306/shop?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&zeroDateTimeBehavior=convertToNull";
    /**
     * 数据库用户名
     */
    public static final String DB_USERNAME = "root";
    /**
     * 数据库密码
     */
    public static final String DB_PASSWORD = "root123";

    /**
     * 获取数据库链接
     *
     * @return
     */
    public String getDBUrl() {
        return DB_URL;
    }

    /**
     * 获取数据库登录名
     *
     * @return
     */
    public String getDBUsername() {
        return DB_USERNAME;
    }

    /**
     * 获取数据库登录密码
     *
     * @return
     */
    public String getDBPassword() {
        return DB_PASSWORD;
    }

    /**
     * 数据库类
     *
     * @return
     */
    public String getDBDriverClass() {
        return DB_DRIVER_CLASS;
    }

    /**
     * 获取生成文件内，写入的包路径
     *
     * @return
     */
    public abstract String getBasePackage();

    /**
     * 获取生成文件保存路径
     *
     * @return
     */
    public abstract String getSavePathPre();

    /**
     * 获取模板文件路径
     *
     * @return
     */
    public abstract String getFtlRelativePath();


    public void generate() {
        try {
            Class.forName(getDBDriverClass());
            // 获取数据库链接
            Connection connection = DriverManager.getConnection(getDBUrl(), getDBUsername(), getDBPassword());
            // 获取key为数据库表名称，value为EntityBean的键值对Map
            Map<String, EntityBean> beanMap = getDatas(connection);
            // 获取生成文件的路径
            String ftlFilePath = this.getClass().getResource("/").getPath() + File.separator + getFtlRelativePath();
            // freemarker配置类
            Configuration cfg = new Configuration();
            cfg.setNumberFormat("#");
            cfg.setDirectoryForTemplateLoading(new File(ftlFilePath));
            // 生成文件
            genFiles(beanMap, cfg);

            System.out.println("ok啦！全部搞定！");
            if (null != connection) {
                connection.close();
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据freemarker配置及bean列表生成文件列表统一接口
     *
     * @param beanMap
     * @param cfg
     * @throws IOException
     */
    protected abstract void genFiles(Map beanMap, Configuration cfg) throws IOException;

    public void genFile(Map beanMap, Configuration cfg, String ftlFileName, String fileNameSuffix) throws IOException {
        genFile(beanMap, cfg, ftlFileName, null, fileNameSuffix);
    }

    /**
     * @param beanMap        数据库转换后的key为数据库表名称，value为EntityBean的键值对Map
     * @param cfg            freemarker配置类
     * @param ftlFileName    对应的freemarker模板文件名称
     * @param savePathPre    保存的文件子路径
     * @param fileNameSuffix 生成的文件后缀名
     * @throws IOException
     */
    public void genFile(Map beanMap, Configuration cfg, String ftlFileName, String savePathPre, String fileNameSuffix) throws IOException {
        if (null != beanMap && beanMap.size() > 0) {
            // 判断子路径字符串是否存在
            boolean incomeSavePathPreFlag = (null == savePathPre || savePathPre.length() <= 0) ? false : true;
            // 迭代beanMap
            Iterator<Map.Entry<String, EntityBean>> iter = beanMap.entrySet().iterator();
            while (iter.hasNext()) {
                Map.Entry<String, EntityBean> mapEntry = iter.next();
                if (null == mapEntry.getKey() || mapEntry.getKey().length() <= 0) {
                    continue;
                }
                // 获取entityBean
                EntityBean classBean = mapEntry.getValue();
                // 获取最终路径
                String savePathPreReal = savePathPre;
                if (!incomeSavePathPreFlag) {
                    savePathPreReal = getSavePathPre() + File.separator + classBean.getClassName() + File.separator;
                }
                // 通过freemarker ftl文件名获取Template类
                Template temEntity = cfg.getTemplate(ftlFileName);
                //判断文件是否存在，存在就删除
                File file = new File(savePathPreReal + File.separator + classBean.getClassName() + fileNameSuffix);
                if (file.exists()) {
                    System.out.println(file.getName() + "  已经存在!删除重建!");
                    file.delete();
                }
                //判断文件夹是否存在
                File newsDir = new File(savePathPreReal);
                if (!newsDir.exists()) {
                    newsDir.mkdirs();
                }
                classBean.setBasePackage(getBasePackage());
                try {
                    // 获取IO流
                    Writer out = new OutputStreamWriter(new FileOutputStream(file.getPath()), "UTF-8");
                    // 生成文件
                    temEntity.process(classBean, out);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 根据数据库链接获取
     *
     * @param connection
     * @return Map<Key, Value> Key是表名 value是表中字段的封装
     */
    public Map<String, EntityBean> getDatas(Connection connection) {
        // 通过DBTableBuildHelper获取数据库表列表
        DBTableBuilder dbTableBuilder = new DBTableBuilder(connection);
        List<DBTableProperty> dbTablePropertyList = dbTableBuilder.buildTableList();
        // 获取转换器
        Transformer Transformer = new Transformer();
        // 通过转换器将数据库表列表转换为实体类EntityBean，一个表对应一个EntityBean
        List<EntityBean> entityBeanList = Transformer.changeTableList(dbTablePropertyList);

        Map<String, EntityBean> map = new HashMap<>(10);
        for (EntityBean entityBean : entityBeanList) {
            // 生成map，key为表名称，value为entityBean
            map.put(entityBean.getTableName(), entityBean);
        }
        return map;
    }

}
